package main

import (
	"fmt"
	"sync"
	"time"
)

//func main() {
//	var ch1, ch2 chan int
//	ch1 = make(chan int)
//	go writerChannel(ch1) // 协程1-通道1
//	ch2 = make(chan int)
//	go writerChannel(ch2) // 协程2-通道2
//	for { // 主goroutine监听其他2个协程，进行交互
//		select {
//		case ch, ok := <-ch1: // 通道读数据
//			if !ok {  // 通道关闭
//				ch1 = nil
//				fmt.Println("ch1 closed")
//				continue
//			}
//			fmt.Println("ch1 reader", ch) // 打印读取数据
//
//		case ch, ok := <-ch2:
//			if !ok {
//				ch2 = nil
//				fmt.Println("ch2 closed")
//				continue
//			}
//			fmt.Println("ch2 reader", ch)
//		default:
//			break
//		}
//		if ch1 == nil && ch2 == nil {
//			break
//		}
//	}
//	fmt.Println("channel finished")
//}
//
//// 通道写数据
//func writerChannel(ch chan int) {
//	for i := 0; i < 100; i++ {
//		ch <- i
//	}
//	close(ch)
//}

var wg sync.WaitGroup

func Producer(c chan int) {
	defer close(c)
	for i := 0; i < 10; i++ {
		c <- i
		fmt.Println("producer:", i)
	}
	wg.Done()
}

func Costomer(c chan int) {
	hasProduct := true
	var p int
	for hasProduct {
		if p, hasProduct = <-c; hasProduct {
			fmt.Println("costomer get:", p)
			continue
		}
	}
	wg.Done()
}

//func main(){
//	c :=make(chan int)
//	wg.Add(2)
//	go Producer(c)
//	go Costomer(c)
//	wg.Wait()
//}

type Product struct {
	mutex     sync.Mutex
	product   chan int
	closeChan chan bool
	isClosed  bool
}

func Producer1(p *Product) {
	defer close(p.product)
	for i := 0; i < 10; i++ {
		p.product <- i
		fmt.Println("producer:", i)
	}

	Close(p)
}

// channel关闭多次，或者使用已关闭的channel会引起panic，这里使用了锁来安全地关闭channel
func Close(p *Product) {
	p.mutex.Lock()
	if !p.isClosed {
		close(p.closeChan)
		p.isClosed = true
	}

	p.mutex.Unlock()
}

func Consumer1(p *Product) {
	var t int
	timeout := make(chan bool, 1)
	go func() {
		time.Sleep(10e9) // sleep one second
		timeout <- true
	}()

	for true {
		select {
		case t = <-p.product:
			fmt.Println("Get:", t);
		case p.isClosed = <-p.closeChan:
			fmt.Println("finished")
			close(p.closeChan)
			break
		case <-timeout:
			fmt.Println("time out")
			break;
		}

		if p.isClosed {
			break
		}
	}

}

func main() {
	p := &Product{
		product:   make(chan int),
		closeChan: make(chan bool),
	}

	go Producer1(p)
	Consumer1(p)
}
